import track, { pauseTracking, resumeTracking } from '../../effect/track.js'
import { TrackOpTypes, isObject, RAW } from '../../utils.js'
import { reactive } from '../../reactive.js'

const arrayInstrumentations = {}
// 重写数组的几个方法
;['includes', 'indexOf', 'lastIndexOf'].forEach((key) => {
  arrayInstrumentations[key] = function (...args) {
    // 1.正常找，此时 this 指向的是代理对象，通过代理对象调用的这些方法
    const res = Array.prototype[key].apply(this, args)

    // 2.找不到
    if (res < 0 || res === false) {
      // 这里就需要拦截器返回原始的对象，this[RAW] 拿到的就是原始对象
      // this[RAW] 访问了代理对象的属性，触发了 get 拦截器，这里返回的是原始对象
      return Array.prototype[key].apply(this[RAW], args)
    }

    // 正常找到
    return res
  }
})

// 重写 push、pop、shift、unshift、splice
// 在调用者几个方法的时候，需要暂停依赖收集，调用完毕之后再恢复
;['push', 'pop', 'shift', 'unshift', 'slice'].forEach((key) => {
  arrayInstrumentations[key] = function (...args) {
    pauseTracking() // 暂停收集依赖
    const res = Array.prototype[key].apply(this, args)
    resumeTracking() // 恢复收集依赖
    return res
  }
})

export default function (target, key) {
  // 这里 RAW 是一个特殊标识，用于返回原始对象
  // 这个标识不能和其他属性重复，所以使用 Symbol 类型
  if (key === RAW) {
    return target
  }

  // 拦截到 get 操作后，要做一些额外的事情
  // 要做的事情，就是收集依赖
  track(target, TrackOpTypes.GET, key)

  // 如果是数组中的某些方法，需要对数组中的方法进行重写
  if (arrayInstrumentations.hasOwnProperty(key) && Array.isArray(target)) {
    // 这些是方法，所以直接返回的是重写后的方法
    return arrayInstrumentations[key]
  }

  const result = Reflect.get(target, key)

  // 获取的成员可能是一个对象，需要进行递归处理，将其转换为响应式
  if (isObject(result)) {
    return reactive(result)
  }
  return result
}
